package {
import flash.display.Sprite;
import flash.display.Bitmap
import flash.geom.Point;
import flash.events.Event;
import flash.events.MouseEvent
import org.papervision3d.core.geom.renderables.Triangle3D;
import org.papervision3d.core.geom.renderables.Vertex3D;
import org.papervision3d.materials.BitmapFileMaterial;
import org.papervision3d.materials.ColorMaterial;
import org.papervision3d.objects.primitives.Plane;
import org.papervision3d.objects.primitives.Sphere;
import org.papervision3d.view.BasicView;
import org.papervision3d.view.stats.StatsView
import org.papervision3d.materials.utils.MaterialsList
import org.papervision3d.core.proto.MaterialObject3D
import org.papervision3d.objects.DisplayObject3D
import org.papervision3d.core.math.Quaternion
import org.papervision3d.core.math.Number3D
import org.papervision3d.materials.BitmapMaterial
import org.papervision3d.render.QuadrantRenderEngine

import org.papervision3d.objects.special.Extruder

import howtodo.Step08BounceMod
import howtodo.Segment2D
import howtodo.BezierUsage

[ SWF( backgroundColor = '#ffffff', width = '800', height = '600', frameRate='60' ) ]
public class Main extends BasicView {
	
	[Embed(source="golfTexture256.jpg")]
	public var texture:Class;
	
	//Grass by Sphinxmorpher http://www.filterforge.com/filters/4739.html
	public const GROUND_TEXTURE:String = "4739+.jpg"
	
	private var ground:Plane;
	private var sphere:Sphere
	private var radius:Number = 20;
	
	private var bezierView:Step08BounceMod;
	private var segmentPlanes:Array
	public static var propMult:Number = 4
	
	private var isDragging:Boolean = false;
	
	private var quadRenderEngine:QuadrantRenderEngine;
	
	public function Main() {
		super(800, 600, false, false, "CAMERA3D");
		preparePv3d();

		bezierView = new Step08BounceMod();
		bezierView.y = 380
		stage.addChild(bezierView);
		bezierView.addEventListener("redraw", reInitPlanes);
		
		createObjects();
		addListeners();
	}
	
	public function preparePv3d():void {
		renderer = quadRenderEngine = new QuadrantRenderEngine(QuadrantRenderEngine.CORRECT_Z_FILTER);
		camera.rotationX = -122
		camera.rotationY = 80
		camera.rotationZ = 180
		camera.x = 32
		camera.y= 534
		camera.z = 462
		addChild(new StatsView(renderer));
	}

	public function createObjects():void {
		ground = new Plane(new BitmapFileMaterial("4739+.jpg"), 700, 700, 6, 6)
		ground.rotationX = 90;
		ground.z = 400
		ground.x = 400
		ground.y = -50;
		scene.addChild(ground)
		
		var bmat:BitmapMaterial = new BitmapMaterial(Bitmap(new texture()).bitmapData, true);
		bmat.tiled  =true;
		
		sphere = new Sphere(bmat, radius, 8, 8)
		scene.addChild(sphere)

		reInitPlanes();
	}
	
	public function reInitPlanes(e:Event = null):void {
		if (segmentPlanes != null) {
			if (segmentPlanes.length == bezierView.segmentCenters.length) {
				addPlanes();	
			}else {
				removePlanes();
				createPlanes();
				addPlanes();
			}
		}else {
			createPlanes();
			addPlanes();
		}
	}
	
	public function removePlanes():void {
		for (var i:int = 0; i < segmentPlanes.length; i++) {
			segmentPlanes[i].materials.removeMaterialByName("front")
			segmentPlanes[i].materials.removeMaterialByName("back")
			segmentPlanes[i].materials.removeMaterialByName("side")
            segmentPlanes[i].materials.removeMaterialByName("all")
            scene.removeChild(segmentPlanes[i])
			destroyDO3D(segmentPlanes[i])
		}
		segmentPlanes = null
	}
	
	public function createPlanes():void {
		segmentPlanes = new Array()
		for (var i:int = 0; i < bezierView.segmentCenters.length; i++) {
			var seg:Segment2D = bezierView.segmentCenters[i]
			var plane:Plane = new Plane(new MaterialObject3D(), seg.length * propMult * 0.9, 100, 0, 0);
			//create extruded plane (Extruder class by J-roen http://www.j-roen.net/papervision/2008/06/12/tutorial-7-vertexes/)
			var depth:Number = 10; 
			var fMat:MaterialObject3D = new ColorMaterial(0xfffcdf);
			var sMat:ColorMaterial = new ColorMaterial(0xbfbca2);
			var mList:MaterialsList = new MaterialsList( { front: fMat, back: fMat, side: sMat } );
			var p1:Point = new Point(plane.geometry.vertices[0].x, plane.geometry.vertices[0].y)
			var p2:Point = new Point(plane.geometry.vertices[1].x, plane.geometry.vertices[1].y)
			var p3:Point = new Point(plane.geometry.vertices[2].x, plane.geometry.vertices[2].y)
			var p4:Point = new Point(plane.geometry.vertices[3].x, plane.geometry.vertices[3].y)
			var extrudedPlane:DisplayObject3D = new Extruder( mList, [p1, p2, p4, p3], depth )
			scene.removeChild(plane);
			destroyDO3D(plane)
			plane = null
			//add our extrudedPlane for scene
			extrudedPlane.rotationY = -seg.angle
			extrudedPlane.x = seg.center.x
			extrudedPlane.z = seg.center.y
			scene.addChild(extrudedPlane)
			segmentPlanes.push(extrudedPlane)
		}
	}
	
	public function addPlanes():void {
		//trace("place", segmentPlanes.length, bezierView.segmentCenters.length)
		for (var i:int = 0; i < segmentPlanes.length; i++) {
			var seg:Segment2D = bezierView.segmentCenters[i]
			var meshA:DisplayObject3D = segmentPlanes[i]
			meshA.rotationY = -seg.angle
			meshA.x = seg.center.x*propMult
			meshA.z = seg.center.y*propMult
		}
	}

	public function addListeners():void {
		stage.addEventListener(MouseEvent.MOUSE_DOWN, onPressHandler);
		stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUpHandler);
		addEventListener(Event.ENTER_FRAME, enterFrameHandler);
	}
	
	public function enterFrameHandler(event:Event):void {
		singleRender();
		
		var vx:Number = bezierView.ball.x*propMult-sphere.x;
		var vy:Number = sphere.y
		var vz:Number = bezierView.ball.y*propMult-sphere.z;
		
		sphere.x = bezierView.ball.x*propMult
		sphere.y = -40
		sphere.z = bezierView.ball.y*propMult
		rotateCamera()
		
		//ease the velocity
		rollMe(vx, vy, vz);
	}
	
	private function rollMe(vx:Number, vy:Number, vz:Number):void{
		var pos:Number3D = new Number3D(sphere.x, sphere.y, sphere.z);
		var dif:Number3D = new Number3D(-vx, -vy, -vz);
		var dist:Number = Math.sqrt(dif.x*dif.x+dif.z*dif.z);
				
		//find the cross of the up axis with the direction vector
		var rotAxis:Number3D = Number3D.cross(dif, new Number3D(0, 1, 0));
		rotAxis.normalize();
		
		//rotate around that axis by how long the direction vector is relative to the radius
		var rotation:Quaternion = Quaternion.createFromAxisAngle(rotAxis.x, rotAxis.y, rotAxis.z, dist/radius);
		rotation.normalize();
		
		sphere.transform.calculateMultiply3x3(rotation.matrix, sphere.transform);
	}
	
	public function onPressHandler(event:MouseEvent):void {
		if(event.target == viewport.containerSprite)
			isDragging = true;
	}
	
	public function onMouseUpHandler(event:MouseEvent):void {
		isDragging = false;
	}
	
	private function rotateCamera():void
	{
		if (isDragging && mouseX < 600) {
			trace(camera, camera.rotationX, camera.rotationY, camera.rotationZ)
			camera.orbit(stage.mouseX - stage.stageWidth/2, stage.mouseY - stage.stageHeight/2, true, ground);
		}
	}
	
	public function destroyDO3D(do3d:DisplayObject3D):void
	{
	   for each ( var aVertex:Vertex3D in do3d.geometry.vertices )
		{
			aVertex.connectedFaces = null ;
			aVertex.vertex3DInstance = null ;
			aVertex.extra = null ;
			aVertex.normal = null ;
		}

		for each ( var aFace:Triangle3D in do3d.geometry.faces )
		{
			aFace.instance = null ;
			aFace.renderCommand = null ;
			aFace.faceNormal = null
			aFace.uv = null ;
			aFace.material = null ;
			aFace.vertices = null ;
			aFace.uv0 = null ;
			aFace.uv1 = null ;
			aFace.uv2 = null ;
			aFace.v0 = null ;
			aFace.v1 = null ;
			aFace.v2 = null ;
		}
		
		do3d.geometry.faces = [] ;
		do3d.geometry.vertices = [] ;
		do3d.geometry = null;
		do3d.faces = [];
		do3d.material.unregisterObject(do3d);
		do3d.material.destroy()
		do3d.materials = null
		do3d = null
	}
}
}
